#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         EntitySource.py
#
# Copyright:    Copyright (c) 2006, Faulkner Technologies
#
# Author:       Brian Oliver
#
# Description:  An 'EntitySource' is a provider of 'Entities', 
#               uniquely identifiable externally defined (outside a Model)
#               concepts/objects/things that are typically 'assessed'.
#
#               Examples of Entities include; people, products, groups
#
#               EntitySources are 'model like' GenericObjects in that
#               they can be placed anywhere in a Portal/the Zope tree.
#
#               Like models, there can be any number of EntitySources
#               in a Portal.
#
#               This class represents a base implementation of an
#               EntitySource.  You should use this to create new
#               EntitySource types.
#
#               In someways, EntitySources are like mini-databases of
#               Entities.
#
#               In the future we expect to have many different 
#               implementations of EntitySources, including those 
#               that permit Entities to be chosen from; LDAP, Databases,
#               Static Lists and Models themselves (permiting assessments
#               to be used as Entities and thus enabling assessment to
#               assessment linking).
#
# See Also:     Entity, *EntityChooser, *EntitySource
#####################################################################

#####################################################################
## KnowledgeEngine Library Imports
from Common import *

#####################################################################
## Zope Library Imports
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.Role import RoleManager
 
#####################################################################
## Python Library Imports
# (none)

#####################################################################
## Knowledge Engine Library Imports
from Common import *
from Core import *
from PropertyDefinition import *
from PropertyTypes import *
from PropertyFormats import *
from PropertyStructures import *
from EventDefinition import *
from GenericObject import GenericObject, GenericObjectInfo
from PermissionDefinition import *

#####################################################################
## CMF Library Imports
from Products.CMFCore import permissions
from Products.CMFCore.ActionInformation import ActionInformation
from Products.CMFCore.utils import getToolByName

#####################################################################
## Zope Library Imports
from Globals import DTMLFile
from Globals import Persistent
from App.ImageFile import ImageFile
from Products.ZSQLMethods.SQL import SQL
from DateTime import DateTime
from AccessControl import ClassSecurityInfo

#####################################################################
## Python Library Imports
import os
import base64
import binascii

#####################################################################
## EntitySourceInfo Class Definition
class EntitySourceInfo(GenericObjectInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """

    #---------------------------------------------------------------#

    def getClassName(self):
        """
            Returns the name of the class for which
            we are providing information.
        """
        return 'EntitySource'

    #---------------------------------------------------------------#

    def getClassFamilyName(self):
        """
            Return the name of the family to which the the
            class (and subclasses) and it's subclasses belong/
            
            This is used to enable searching for families of 
            generic objects, eg: 'EntitySource' instances.
        """

        #all entity sources belong to the 'EntitySource' family
        return 'EntitySource'

    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """

        return "EntitySource"

    #---------------------------------------------------------------#

    def getDescription(self):
        """
            ABSTRACT: Return a friendly description on the purpose
            of this class.  This description is often used in the
            Property Editor for instances of this class
        """

        return "EntitySources define the standard schema and provide Entities for assessments."

    #---------------------------------------------------------------#

    def getIdentityType(self):
        """
            Return the identity type.
        """

        return "EntitySource"

    #---------------------------------------------------------------#

    def getClassIcon(self):
        """
            Return the appropriate icon file for the class.
        """

        return ImageFile('skins/EntitySource_icon.gif', globals())

    #---------------------------------------------------------------#

    def isContainer(self):
        """
            Returns if the instance is providing folderish/container
            services.

            Override an return true if the instance is to be
            a container for other instances.

            NOTE: all instances support "events" so there is no
            need to return true if you require "event" support.
        """

        return false

    #---------------------------------------------------------------#

    def isPermittedInContainer(self, containerClassInfo, containerInstance = None, instance=None):
        """
            ABSTRACT: Returns if class instances (instance) is permitted in
            the specified container (containerInstance)

            NOTE: This is from this class' instance perspective.
        """

        #TODO: EntitySources are only permitted in CMF Folders!!!
        return true

    #---------------------------------------------------------------#

    def isChildPermitted(self, childClassInfo, childInstance = None):
        """
            Returns if the specified child is permitted as a
            child of the class' instances.

            NOTE: This is from this class' instance perspective.

            For component containers, this always overrides what the
            return value from childClassInfo.isPermittedInContainer(...)
        """

        #EntitySources don't contain anything
        return false

    #---------------------------------------------------------------#

    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific properties using the PropertyReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class properties for reflection
        GenericObjectInfo.onConfigureProperties(self)

    #---------------------------------------------------------------#

    def onConfigureEvents(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific events using the EventReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class events for reflection
        GenericObjectInfo.onConfigureEvents(self)

    #---------------------------------------------------------------#

    def onConfigurePermissions(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific permission definitions.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class permissions for reflection
        GenericObjectInfo.onConfigurePermissions(self)

        
        # Entity display template. Can be overidden in an entity chooser
        # cmoponent
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="entityDisplayTemplate",
                displayName="Entity Display Template",
                description="The name of the template to be used to display the entity in assessments and reports",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_SINGLELINE,
                defaultValue="",
            )
        )

    #---------------------------------------------------------------#

#####################################################################
## EntitySource Class Definition
class EntitySource(GenericObject):
    """
        Base container for all Faulkner KnowledgeEngine Components
    """

    security = ClassSecurityInfo()

    #---------------------------------------------------------------#

    #ABSTRACT TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = EntitySourceInfo()

    #---------------------------------------------------------------#
    # Default Attribute Values
    meta_type = 'EntitySource'

    #---------------------------------------------------------------#

    def __init__(self, id, identity):
        "Constructor for the object"

        GenericObject.__init__(self, id, identity)

        #perform onAfterInstanciation
        self.onAfterInstanciation()

    #---------------------------------------------------------------#

    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object
            has been loaded into memory.

            This is useful for performing backwards compatibility
        """

        #perform default onAfterInstanciation for the container
        GenericObject.onAfterInstanciation(self)

    #---------------------------------------------------------------#

    def getPortalObject(self):
        """
            Return the portal level object (like a Model or EntitySource)
            in which this object is located
        """

        return self


    #---------------------------------------------------------------#
    def getNrEntities(self, user = None, filter = {}):
        """
            ABSTRACT: Returns the number of entities available from this
            EntitySource for the specified user.
            
            The user (optional) is provided to enable EntitySources to filter by user.
            
            The filter is a dict (key / value) used as a simple filter.
            TODO: Filtering needs to be reworked
        """
        
        return 0

    #---------------------------------------------------------------#

    def getEntities(self, user = None, filter = {}):
        """
            ABSTRACT: Returns the entities from this entity source,
            ordered by entity identity.
            
            The filter is a dict (key / value) used as a simple filter.
            TODO: Filtering needs to be reworked
        """
        
        return []

    #---------------------------------------------------------------#

    def getEntitiesByBatch(self, batchNr, batchSize = 10, user = None, filter = {}):
        """
            ABSTRACT: Returns a batch of entities as specified.
            
            NOTE 1: The first available batch is 0
            
            NOTE 2: The entities are returned in order of their name
            
            The filter is a dict (key / value) used as a simple filter.
            TODO: Filtering needs to be reworked
        """
        
        return []

    #---------------------------------------------------------------#

    def getEntity(self, entityIdentity, user = None):
        """
            ABSTRACT: Returns the entity with the specified identity
            for the provided user
        """
        
        return None


    #---------------------------------------------------------------#
    def getEntityDisplayTemplate(self):
        """
            Returns the template used to display this entity
        """
        
        if self.entityDisplayTemplate:
            return self.entityDisplayTemplate
        
        # Default Entity display template
        return "Entity_display"



    #---------------------------------------------------------------#

    def hasDefaultView(self):
        """
            PLONE Support:

            Returns if the object has a default view.  By setting this
            to true, a view tab will be added to the object.

            If this is false, attempting to visit the / (default view)
            for a component will redirect the client to the
            next available parent that has a default view.
        """

        return false
        
    #---------------------------------------------------------------#

    def getEntityNameSingular(self):
        """
            ABSTRACT: Return the name of a single entity provided 
            by the Entity Source
        """
        
        return "Entity"
        
    #---------------------------------------------------------------#

    def getEntityNamePlural(self):
        """
            ABSTRACT: Return the name of a collection of entities provided 
            by the Entity Source
        """
        
        return "Entities"
        
    #---------------------------------------------------------------#

    def getBoundEntityChoosers(self):
        """
            FINAL: Returns a list of EntityChoosers that are 
            bound to this EntitySource.  
            
            We can use this method to determine the Models
            in which this EntitySource is used.
        """
        
        #we need to use the KE catalog to find the EntityChoosers
        catalog = getToolByName(self, 'knowledgeengine_genericobjectcatalogservice', None)
        
        #return the EntityChoosers that use this EntitySource
        choosers = []
        for catalogObject in catalog(getClassFamilyName = 'EntityChooser'):
            if catalogObject.getObject().getEntitySource().getIdenty() == self.getIdentity():
                choosers.append(catalogObject.getObject())
        
        return choosers
        
    #---------------------------------------------------------------#

    def getBoundModels(self):
        """
            FINAL: Returns a list (with no duplicates) of Models that are 
            "bound to"/"require" this EntitySource.  
        """

        #grab the entitychoosers... we'll find the models from there
        entityChoosers = self.getBoundEntityChoosers()
        
        #get the models 
        models = []
        for entityChooser in entityChoosers:
            if not entityChooser.getModel() in models:
                models = models + [entityChooser.getModel()]
                
        return models                
        
    #---------------------------------------------------------------#

#####################################################################
## Class Initialisation
# (none)
